﻿namespace WebPacker.Tests.Packs.Watchers
{
	using System;
	using System.Collections.Generic;
	using System.IO;
	using System.Linq;
	using NUnit.Framework;
	using WebPacker.Packs.Resources;
	using WebPacker.Packs.Watchers;

	[TestFixture]
	public abstract class ResourceWatcherBaseTest
	{
		[Test]
		public void AcceptTest()
		{
			using (var monitor = GetMonitor())
			{
				Assert.IsTrue(monitor.Accept(GetTestResource()));
			}
		}

		[Test]
		public void AcceptFalseTest()
		{
			using (var monitor = GetMonitor())
			{
				Assert.IsFalse(monitor.Accept(GetNotAcceptableResource()));
			}
		}

		[Test]
		public void AddEmptyTest()
		{
			using (var monitor = GetMonitor())
			{
				Assert.Throws<ArgumentNullException>(() => monitor.Add(null));
			}
		}

		[Test]
		public void AddNotAcceptableTest()
		{
			using (var monitor = GetMonitor())
			{
				Assert.Throws<ArgumentException>(() => monitor.Add(GetNotAcceptableResource()));
			}
		}

		[Test]
		public void AddTest()
		{
			using (var monitor = GetMonitor())
			{
				monitor.Add(GetTestResource());
				Assert.AreEqual(1, monitor.GetResources().Count());
				monitor.Add(GetTestResource());
				Assert.AreEqual(2, monitor.GetResources().Count());
			}
		}

		[Test]
		public void AddDuplicateTest()
		{
			using (var monitor = GetMonitor())
			{
				// it's normal..
				monitor.Add(GetTestResource());
				monitor.Add(GetTestResource());
			}
		}

		[Test]
		public void AddDuplicateObjectTest()
		{
			using (var monitor = GetMonitor())
			{
				var resource = GetTestResource();
				monitor.Add(resource);
				Assert.Throws<ArgumentException>(() => monitor.Add(resource));
			}
		}

		[Test]
		public void GetResourcesTest()
		{
			using (var monitor = GetMonitor())
			{
				var resources = monitor.GetResources().ToArray();
				var resource = GetTestResource();
				Assert.AreEqual(0, resources.Length);
				monitor.Add(resource);
				resources = monitor.GetResources().ToArray();
				Assert.AreEqual(1, resources.Length);
				Assert.AreEqual(resource, resources[0]);
			}
		}

		[Test]
		public void ContainsTest()
		{
			using (var monitor = GetMonitor())
			{
				var resource = GetTestResource();
				Assert.IsFalse(monitor.Contains(resource));
				monitor.Add(resource);
				Assert.IsTrue(monitor.Contains(resource));
			}
		}

		[Test]
		public void RemoveTest()
		{
			using (var monitor = GetMonitor())
			{
				var resource = GetTestResource();
				monitor.Add(resource);
				Assert.IsTrue(monitor.Remove(resource));
				var resources = monitor.GetResources().ToArray();
				Assert.IsNotNull(resources);
				Assert.AreEqual(0, resources.Length);
			}
		}

		[Test]
		public void RemoveNotExistedTest()
		{
			using (var monitor = GetMonitor())
			{
				Assert.IsFalse(monitor.Remove(GetTestResource()));
			}
		}

		[Test]
		public void SetTest()
		{
			using (var monitor = GetMonitor())
			{
				var items = GetTestResources();
				monitor.Set(items);
				var resources = monitor.GetResources();
				Assert.That(resources, Is.EquivalentTo(items));
			}
		}

		[Test]
		public void SetNotAcceptableTest()
		{
			using (var monitor = GetMonitor())
			{
				var items = new List<IResource>();
				items.AddRange(GetTestResources());
				items.Add(GetNotAcceptableResource());
				Assert.Throws<ArgumentException>(() => monitor.Set(items.ToArray()));
				var resources = monitor.GetResources().ToArray();
				Assert.AreEqual(0, resources.Length);
			}
		}

		[Test]
		public void ClearTest()
		{
			using (var monitor = GetMonitor())
			{
				monitor.Set(GetTestResources());
				monitor.Clear();
				Assert.AreEqual(0, monitor.GetResources().Count());
			}
		}

		protected abstract IResourceWatcher GetMonitor();

		protected abstract IResource GetTestResource();

		protected abstract IResource[] GetTestResources();

		protected abstract IResource GetNotAcceptableResource();

		protected void ModifyFile(string fileName)
		{
			using (var writer = File.AppendText(fileName))
			{
				writer.Write(" ");
				((FileStream)writer.BaseStream).Flush(true);
			}
		}
	}
}
